#define _CRT_SECURE_NO_WARNINGS 1


		《 Linux_15_文件路径 》



		《 chdir 可以更改进程所处路径 》

[a@192 c]$ man chdir
------------------------------------------------------------------
NAME
chdir, fchdir - change working directory更改工作目录

SYNOPSIS
    #include <unistd.h>

    int chdir(const char* path);
    int fchdir(int fd);
------------------------------------------------------------------
示例： chdir("/home/a/Desktop/a");     //更改路径后，创建新建文件就在更改后的路径下，但是当前路径不变！

——————————————————————————————————————————————————————————————————


		《 Makefile 》

bin = myfile
src = myfile.c

$(bin) :$(src)
	@gcc -o $@ $^
.PHONY:clean
clean :
	@rm - f $(bin)


		《 myfile.c 》

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    chdir("/home/a/Desktop/a");     //更改工作目录后，创建新建文件就在更改后的路径下，但是当前程序所处路径不变！

    FILE* fp = fopen("log.txt", "w");//以 写 的方式打开 log.txt
    (void)fp; // ingor warning      //小细节： 创建的变量 不使用，有些编译器会 警告！ 忽略警告。
    fclose(fp);                     //关闭文件

    while (1)
    {
        printf("I am a process! PID: %d\n", getpid());
        sleep(1);
    }

    return 0;
}


	< 退回之前路径: cd - >
[a@192 a]$ cd -
 home / a / Desktop / a / c


 执行程序，PID ： 3679
[a@192 c]$ . / myfile
I am a process!PID: 3679

 查看 PID: 所在位置
[a@192 ~]$ ll / proc / 3679
total 0

于是发现 当前路径 已经被更改，新建文件时就是在当前目录新建。
lrwxrwxrwx. 1 a a 0 Mar 26 08:10 cwd -> / home / a / Desktop / a

//执行程序所在路径：
lrwxrwxrwx. 1 a a 0 Mar 26 08 : 09 exe -> / home / a / Desktop / a / c / myfile



——————————————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————————————

		《 进程状态 》
static const char* const task_state_array[] = 
{ 
	"R(running"，		/* 0 */		// 运行状态
	"S (sleeping)"，	    /* 1 */		// 休眠状态   默认为S+ 前台运行, 可执行程序后面加 & 就是后台运行 S 状态                           
	"D (disk sleep)"，	/* 2 */		// 磁盘睡眠 ：深度睡眠，不可被杀
	"T (stopped)"，		/* 4 */		// 暂停
	"t (tracing stop)"，/* 8 */		// 因追踪而暂停：比如调试时
	"X (dead)"，		    /* 16*/     // 死亡状态(任务表里看不到这个状态)
	"Z (zombie)",		/* 32*/      // 僵尸状态
};


1.直接谈论Linux的进程状态
struct task_struct
{
	// ...
	//内部的一个属性
	int status;
};

定义状态...
#define RUN 1
# define SLEEP 2
#define STOP 3

——————————————————————————————————————————————————————————————————


		《 Makefile 》

bin = myfile
src = myfile.c

$(bin) :$(src)
	@gcc -o $@ $^
.PHONY:clean
clean :
	@rm - f $(bin)

		< 拷贝文件 >
 拷贝 上级目录 C 目录里的 Makefile 到当前路径
 [a@192 test_status]$ cp .. / c / Makefile .

拷贝成功，打开
[a@192 test_status]$ vim Makefile


		《 文本替换 》

 Esc :  进入命令模式： 后者替换前者
:% s / myfile / testStatus /


——————————————————————————————————————————————————————————————————


		《 进程状态实测 》

int main()
{
    while (1)
    {
        printf("I am a process! pid: %d\n", getpid());
    }
    return 0;
}


	【 R ：进程运行时状态 】
	【 S ：可中断睡眠状态 】

		< S+ 状态 > 
// 大部分都是 S+  
//因为 cpu 速度比 显示器刷新快，大部分时间在等待显示器准备就绪，
//或者说 进程处于：休眠状态 ==> 进程在等待"资源"就绪
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
4891   5554   5554   4891 pts / 1      5554 S+ 1000   0:01 ./testStatus         // S+ ：前台运行

4891   5554   5554   4891 pts / 1      5554 S 1000   0:01 ./testStatus &        // S  ：后台运行 运行程序时 后面带 & 就进入后台运行该可执行程序 (后台运行的程序无法直接终止掉，需要杀掉：kill -9 pid)

//...
		< R+ 状态 >
//偶尔出现 R +     // 准备 就绪，向显示器输出 内容 
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
4891   5554   5554   4891 pts / 1      5554 R+ 1000   0 : 01 ./testStatus


 如果不向 屏幕输出内容，
 while (1)  { } 
 只调用 cpu 资源，就一直是 R+ 状态。

——————————————————————————————————————————————————————————————————

		《 前台运行 》
[a@192 test_status]$ . / testStatus
 
		《 进程后台运行 & 》
		< S 状态 >
 S 状态 没有 ‘+’号
[a@192 test_status]$ . / testStatus &
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
4891   5554   5554   4891 pts / 1      5554 S  1000   0:01 . / testStatus

		
//后台运行 无法 Ctrl + C 终止
I am a process!pid: 5915
//...
//需要直接杀死 kill -9
[a@192 test_status]$ kill - 9 5915
[1] + Killed                  . / testStatus


——————————————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————————————


		《 Linux 信号 》

查看进程信号： kill -l 

[a@192 test_status]$ kill -l
1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN + 1	36) SIGRTMIN + 2	37) SIGRTMIN + 3
38) SIGRTMIN + 4	39) SIGRTMIN + 5	40) SIGRTMIN + 6	41) SIGRTMIN + 7	42) SIGRTMIN + 8
43) SIGRTMIN + 9	44) SIGRTMIN + 10	45) SIGRTMIN + 11	46) SIGRTMIN + 12	47) SIGRTMIN + 13
48) SIGRTMIN + 14	49) SIGRTMIN + 15	50) SIGRTMAX - 14	51) SIGRTMAX - 13	52) SIGRTMAX - 12
53) SIGRTMAX - 11	54) SIGRTMAX - 10	55) SIGRTMAX - 9	56) SIGRTMAX - 8	57) SIGRTMAX - 7
58) SIGRTMAX - 6	59) SIGRTMAX - 5	60) SIGRTMAX - 4	61) SIGRTMAX - 3	62) SIGRTMAX - 2
63) SIGRTMAX - 1	64) SIGRTMAX
[a@192 test_status]$

//     《 进程暂停信号 19 》
[a@192 test_status]$ kill -19 5915
 进程暂停			// 比如【调试】时 就是通过让进程暂停来实现的
 - - - - - - 
//     《 进程继续信号 18 》
 继续进程
[a@192 test_status]$ kill -18 5915

——————————————————————————————————————————————————————————————————


		< 调试信息 >
 gcc -o $@ $^ -g 
 [a@192 test_status]$ readelf -S myprocess | grep debug
[26].debug_aranges  PROGBITS   0000000000000000  000030a5
[27].debug_info     PROGBITS   0000000000000000  000030d5
[28].debug_abbrev   PROGBITS   0000000000000000  000034c8
[29].debug_line     PROGBITS   0000000000000000  00003603
[30j.debug_str      PROGBITS   0000000000000000  0000372a
[a@192 test_status]$


// 进入 GDB调试 模式 
//gdb myprocess 
//展示代码：
(gdb)l 0

1	#include <stdio.h>
2	#include <sys/types.h>
3	#include <unistd.h>
4
5	int main()
6	{
7		while (1)
8		{
9			printf("I am a process! pid: %d\n", getpid());
10		}
(gdb)
11		return 0;
12	}
(gdb)
(gdb) b 10	//	 第10行打断点

// ...
//		《 T/t 暂停进程状态 》 
//让进程暂停，等待被进一步唤醒。

(gdb) r		//运行，碰到断点，会停下来,此时状态是: 't'   // 遇到追踪而暂停
//PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
//4891   5554   5554   4891 pts / 1      5554 t  1000   0:01 . / testStatus
// ...

(gdb) c		//继续运行,状态又变成：‘S+’
//PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
//4891   5554   5554   4891 pts / 1      5554 S+  1000   0:01 . / testStatus



——————————————————————————————————————————————————————————————————

		< D 状态 >
 D 状态：不可被杀，深度睡眠，不可中断睡眠 (比如传输数据时：防止数据丢失，该进程设为 D 状态)
 如何唤醒 D 状态？
 1.进程自己醒来，比如数据传输完成后
 2.重启，甚至 ~ 断电 
 一般不会出现 D 状态，要么是 服务器 I/O 压力太大了， 要么机器快挂掉了



——————————————————————————————————————————————————————————————————

		< Z 状态 > 
 僵尸状态
 已经运行完毕，但是需要维持自己的退出信息，
 在自己的 task_struct 会记录自己的退出信息，未来让父进程来进行读取
 如果没有父进程读取，僵尸进程会一直存在！

 进程 = 内核数据结构 task_struct + 进程的代码数据
 进程的代码数据 可以被释放掉，
 task_struct  会被一直维持，直到父进程来读取。如果不进行读取，就存在内存泄漏问题。
 man waitpid  用等待接口对进程 来进行等待。
 如果进行读取了， 由 Z状态 变为 X状态 由OS操作系统进行释放。

		《 Makefile 》
bin = testD
src = testD.c

$(bin) :$(src)
@gcc - o $@ $ ^
.PHONY:clean
clean :
@rm - f $(bin)

		《 testD.c 》
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        // child
        int cnt = 5;
        while (cnt)
        {
            printf("I an child! cnt: %d, PID: %d\n", cnt, getpid());
            sleep(1);
            cnt--;
        }
    }
    else
    {
        // parent
        while (1)
        {
            printf("I an parent, running always! PID: %d\n", getpid());
            sleep(1);
        }
    }

    return 0;
}


I an parent, running always!PID: 3476
I an child!cnt : 1, PID : 3477
I an parent, running always!PID : 3476

 ...
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
3087   3476   3476   3087 pts / 1      3476 S + 1000   0:00 . / testD
3476   3477   3476   3087 pts / 1      3476 S + 1000   0 : 00 . / testD
 ...

 S 状态变为 Z 状态  
 [a@192 d]$ while :; do ps axj | head -1 && ps axj | grep testD | grep -v grep; sleep 1;done
 ...
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
3087   3476   3476   3087 pts / 1      3476 S + 1000   0 : 00 . / testD
3476   3477   3476   3087 pts / 1      3476 Z + 1000   0 : 00[testD] <defunct> 无效的
 ...


——————————————————————————————————————————————————————————————————

		《 孤儿进程 》
 父进程比子进程先退出了，子进程就是孤儿进程

I an parent, running always!PID: 4051
I an child!cnt : 1, PID : 4052
I an child!cnt : 1, PID : 4052
[a@192 d]$ I an child!cnt : 1, PID : 4052
I an child!cnt : 1, PID : 4052
I an child!cnt : 1, PID : 4052
I an child!cnt : 1, PID : 4052
  ...
[a@192 d]$ kill - 9 4052                                                            //孤儿进程是 可以被杀掉的

  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
  2446   5041   5041   2446 pts/0      5041 S+    1002   0:00 ./testStatus.exe      //父进程 (先退出了)
  5041   5042   5041   2446 pts/0      5041 S+    1002   0:00 ./testStatus.exe      //子进程 (父进程先退出后，子进程会成为孤儿进程)
  PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
     1   5042   5041   2446 pts/0      2446 S     1002   0:00 ./testStatus.exe      //孤儿进程 由 S+ 变为 S(后台运行), 孤儿进程是 可以被杀掉的


父进程如果先退出，子进程就会变成 孤儿进程。
孤儿进程一般都是会被 1号进程(OS本身) 进行领养的
孤儿进程为什么要被OS领养? 
依旧要保证 子进程 正常被回收

为什么我们平时写代码，没见过僵尸进程 Z ？(没有关系过 内存泄漏)
直接在 命令行中 启动的进程，他的父进程是 bash， 
bash 会自动回收新进程的Z


——————————————————————————————————————————————————————————————————

			< 并发/并行 >
一个进程一旦持有CPU，会一直运行到这个进程结束吗 ? ?
不会

【并发】：
基于时间片进行轮转调度的
让多个进程以切换的方式进行调度，在一个时间段内同时得以推进代码，
就叫做 并发

【并行】：
任何时刻，都同时有多个进程在真的同时运行，我们叫做 并行

——————————————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————————————


		《 阻塞 S 》

比如： scanf("%d " & a); //需要等待键盘资源的状态,就是阻塞状态

不是只有 cpu 才有运行队列哦
各种设备也有自己的 wait_queue

阻塞 和 运行 的状态变化，往往伴随着 pcb 被连入 不同的队列中!
入队列的 不是进程的什么代码和数据 而是进程的 task_struct


#define KEY_BOARD 1
#define SCREEN 2
struct device
{
	int type;
	int status;
	//其他属性
	// ...
	struct device* next;  // --> 把它们链接起来
	task_struct* wait_queue;
}

//唤醒：
//把 wait_queue 等待队列中的 task_struct 重新链入到进程的运行队列中，我们称之为 唤醒！


//      《 task_struct 》
//进程的 task_struct 内容有很多，以及嵌套的 struct
struct task_struct 
{
    volatile long state;/* -1 unrunnable，0 runnable, >0 stopped */void *stack;
    atomic_t usage;
    unsigned int flags; /* per process flags, defined below*/unsigned int ptrace;
    int lock_depth;
    /*BKL lock depth*/
    //...
    //很大！！！ 
    //...
    pid_t pid;
    pid_t tgid;
    //...
};

		《 挂起 》
阻塞挂起状态
task_struct 还在队列中，但是它的 代码和数据被换出到磁盘了，
比如内存不足时，就可以使用原本 代码和数据所占用的内存空间了，
当运行到该 进程时，再进行换入，再从磁盘吧代码和数据拷贝到内存。
 挂起可以更合理的使用内存资源。


		< swaps分区 >
 swaps分区 OS挂起时的空间大小，不宜过大，避免操作系统过度依赖 swap 分区，会导致效率问题。 
ls -l /proc/swaps       //swaps 分区所处位置

fdisk (工具) 用来 磁盘的区域划分和写入文件系统的 ;是一个创建和维护分区表的程序

——————————————————————————————————————————————————————————————————


		《 进程的切换 》

CPU内部有许多寄存器(各种寄存器：
64位通用  寄存器：RAX(用于存储整数数据和指针)， RBX, RCX, RDX, R8-R15(X86_64新增))
指针和索引 寄存器：RSP(堆栈指针寄存器，用于指向当前堆栈顶部)， RBP， RSI, RDI ... 
       段 寄存器：CS, DS ...
...

CPU内部的所有的寄存器中的临时数据，叫做进程的上下文

进程在切换，最重要的一件事情是:上下文数据的保护和恢复

CPU内的寄存器 :
寄存器本身是硬件，具有数据的存储能力，CPU的寄存器硬件只有一套!!
CPU内部的数据，可以有多套，有几个进程，就有几套和该进程对应的上下文数据
寄存器 != 寄存器的内容

//  《 寄存器 的值 》
// 在(老内核中)：进程 PCB 结构体中(末尾)，嵌套着寄存器的结构体
struct task_struct
{
     struct tss_struct tss; //寄存器 的值
}

//任务状态段数据结构： (寄存器 的值)
struct tss_struct tss
{
    long back_link;  /* 16 high bits zero */
    long esp0;
    long sso;        /* 16 high bits zero */
    long esp1;
    long ss1;        /*16 high bits zero */
    long esp2;
    long ss2;        /* 16 high bits zero */
    long cr3;
    long eip;long eflags;
    long eax,ecx,edx,ebx;
    ...
}

——————————————————————————————————————————————————————————————————


——————————————————————————————————————————————————————————————————